// CarSimDisplayDlg.cpp : implementation file
//

#include "stdafx.h"
#include "CarSimDisplay.h"
#include "CarSimDisplayDlg.h"
#include ".\carsimdisplaydlg.h"

#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include <math.h>

#include "Robot.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif



Robot g_MyRobot(10.0, 10.0, 0.0, 0.0, 0.0);

class glMatrixScope
{
public:
	glMatrixScope()
	{
		glPushMatrix();
	}

	~glMatrixScope()
	{
		glPopMatrix();
	}
};

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CCarSimDisplayDlg dialog



CCarSimDisplayDlg::CCarSimDisplayDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CCarSimDisplayDlg::IDD, pParent)
{
	m_bPlaybackRunning = false;
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCarSimDisplayDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_BUTTON_TO_DRAW_ON, m_buttonToDrawOn);
	DDX_Control(pDX, IDC_SLIDER_STEERING, m_sliderSteering);
	DDX_Control(pDX, IDC_EDIT_NUM_OBSTACLES, m_textboxNumObstacles);
	DDX_Control(pDX, IDC_EDIT_NUM_SIDES_PER_OBSTACLE, m_textboxSidesPerObstacle);
	DDX_Control(pDX, IDC_EDIT_RAD_PER_OBS, m_textboxRadiusPerObstacle);
	DDX_Control(pDX, IDC_EDIT_NUM_TRAINING_ITERATIONS, m_editNumTrainingRuns);
	DDX_Control(pDX, IDC_EDIT_EVAL_FREQ, m_editEvalFreq);
	DDX_Control(pDX, IDC_EDIT_LEARNING_RATE, m_editLearningRate);
	DDX_Control(pDX, IDC_QLEARN, m_checkboxUseQlearning);
	DDX_Control(pDX, IDC_EDIT_REWARD_DISCOUNT, m_editDiscountFactor);
	DDX_Control(pDX, IDC_EDIT_MAGIC_ANNEAL, m_editMagicAnneal);
	DDX_Control(pDX, IDC_CHECK_ROBOT_TRAILS, m_checkboxTrails);
}

BEGIN_MESSAGE_MAP(CCarSimDisplayDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDOK, OnBnClickedOk)
	ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel)
	ON_BN_CLICKED(IDC_BUTTON_TO_DRAW_ON, OnBnClickedButtonToDrawOn)
	ON_BN_CLICKED(IDC_BUTTON_FORWARDS, OnBnClickedButtonForwards)
	ON_BN_CLICKED(IDC_BUTTON_BACKWARDS, OnBnClickedButtonBackwards)
	ON_BN_CLICKED(IDC_BUTTON_GEN_OBSTACLES, OnBnClickedButtonGenObstacles)
	ON_BN_CLICKED(IDC_BUTTON_RUN_BEHAVIOR, OnBnClickedButtonRunBehavior)
	ON_BN_CLICKED(IDC_BUTTON_STOP_PLAYBACK, OnBnClickedButtonStopPlayback)
	ON_BN_CLICKED(IDC_BUTTON_TRAIN, OnBnClickedButtonTrain)
	ON_BN_CLICKED(IDC_BUTTON_SAVE_NNET, OnBnClickedButtonSaveNnet)
	ON_BN_CLICKED(IDC_BUTTON_LOAD_NNET, OnBnClickedButtonLoadNnet)
	ON_BN_CLICKED(IDC_BUTTON_RESET_ROBOT, OnBnClickedButtonResetRobot)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CCarSimDisplayDlg::CarSimDrawButton, CButton)
	ON_WM_CREATE()
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CCarSimDisplayDlg::OnTimer(UINT_PTR nIDEvent)
{
	if(m_bPlaybackRunning && nIDEvent == GetPlaybackTimer())
	{
		bool bHitGoal;

		bool bTrails = m_checkboxTrails.GetCheck();

		if(!bTrails)
		{
			g_MyRobot.ClearTrails();
		}
		TRACE("Running playback\n");
		bool bWorked = g_MyRobot.RunRobotOneStep(bHitGoal);
		m_buttonToDrawOn.DrawScreen();
		if(!bWorked)
		{
			
			TRACE("Collided: Stopped playback\n");
			m_bPlaybackRunning = false;
			KillTimer(nIDEvent);

			if(bHitGoal)
			{
				AfxMessageBox("Claims to have hit goal\n");
			}
		}
	}
	// CWnd::OnTimer(nIDEvent);
}
// CCarSimDisplayDlg message handlers

BOOL CCarSimDisplayDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here

	// m_buttonToDrawOn.InitOglStuff();
	
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CCarSimDisplayDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}



GLvoid initializeGL(GLsizei width, GLsizei height) 
{ 
    GLfloat     aspect; 
    GLdouble    near_plane, far_plane; 


 
    // glClearIndex( (GLfloat)BLACK_INDEX); 
	glClearColor((GLclampf)0.0, (GLclampf)0.0, (GLclampf)0.0, (GLclampf)0.0);
    // glClearDepth( 1000.0 ); 
  
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_COLOR_MATERIAL);
 
    glMatrixMode( GL_PROJECTION );
	// glLoadIdentity();
    aspect = (GLfloat) width / height; 
	
    // gluPerspective( 45.0, aspect, 3.0, 7.0 ); 
	// glOrtho(-100.0f, windowWidth, -100.0f, windowHeight, -200.0f, 200.0f);
	glOrtho(-100.0*aspect, 100.0*aspect, -100.0, 100.0, -10.0, 1000.0);
    glMatrixMode( GL_MODELVIEW ); 
 
    near_plane = 10.0; //3.0; 
    far_plane = 1000.0; //7.0; 
 
	gluLookAt(0.0, 0.0, 150.0, // eye
		0.0, 0.0, 0.0, // center
		0.0, 1.0, 0.0); // up
    //latitude = 0.0F; 
    //longitude = 0.0F; 
    //latinc = 6.0F; 
    //longinc = 2.5F; 
 
    // createObjects(); 
}

BOOL CCarSimDisplayDlg::CarSimDrawButton::SetupPixelFormat() 
{ 
	static bool bPixelsSetUp = false;

	if(bPixelsSetUp)
	{
		return TRUE;
	}
	 
    PIXELFORMATDESCRIPTOR pfd, *ppfd; 
    int pixelformat; 
 
    ppfd = &pfd; 
 
    ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); 
    ppfd->nVersion = 1; 
    ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |  
                        PFD_DOUBLEBUFFER; 
    ppfd->dwLayerMask = PFD_MAIN_PLANE;  // PFD_OVERLAY_PLANE
    ppfd->iPixelType = PFD_TYPE_RGBA; 
    ppfd->cColorBits = 32; 
    ppfd->cDepthBits = 32; 
    ppfd->cAccumBits = 0; 
    ppfd->cStencilBits = 0; 

	PIXELFORMATDESCRIPTOR test_pfd = { 
		sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd 
		1,                     // version number 
		PFD_DRAW_TO_WINDOW |   // support window 
		PFD_SUPPORT_OPENGL |   // support OpenGL 
		PFD_DOUBLEBUFFER,      // double buffered 
		PFD_TYPE_RGBA,         // RGBA type 
		24,                    // 24-bit color depth 
		0, 0, 0, 0, 0, 0,      // color bits ignored 
		0,                     // no alpha buffer 
		0,                     // shift bit ignored 
		0,                     // no accumulation buffer 
		0, 0, 0, 0,            // accum bits ignored 
		32,                    // 32-bit z-buffer 
		0,                     // no stencil buffer 
		0,                     // no auxiliary buffer 
		PFD_MAIN_PLANE,        // main layer PFD_OVERLAY_PLANE, //
		0,                     // reserved 
		0, 0, 0                // layer masks ignored 
	};

	ppfd = &test_pfd;
	// figure out how to actually do this later

	CDC * pCDC = GetDC();

	if(!pCDC)
	{
		return FALSE;
	}

	HDC hdcTest = pCDC->GetSafeHdc();
	
    pixelformat = ChoosePixelFormat(pCDC->GetSafeHdc(), ppfd); 

	DWORD pixError = GetLastError();

	DWORD pixGetError = 0;
	if(!pixelformat)
	{
		pixelformat = ::GetPixelFormat(pCDC->GetSafeHdc());
		pixGetError = GetLastError();
	}
 
    if ( (pixelformat = ChoosePixelFormat(GetDC()->GetSafeHdc(), ppfd)) == 0 ) 
    { 
        TRACE("ChoosePixelFormat failed %d\n", pixGetError);// "Error", MB_OK); 
        return FALSE; 
    } 
 
    if (SetPixelFormat(GetDC()->GetSafeHdc(), pixelformat, ppfd) == FALSE) 
    { 
        TRACE("SetPixelFormat failed %d\n", "Error", pixGetError);// "Error", MB_OK); 
        return FALSE; 
    }
 
	// MessageBox("Pixel Format stuff succeeded");

	bPixelsSetUp = true;

    return TRUE; 
} 

HGLRC CCarSimDisplayDlg::CarSimDrawButton::InitOglStuff()
{
	/*if(!m_bOglInitialized)
	{*/
	// TRACE("Initialized OGL stuff\n");
	// m_ghDC = GetDC(); 
    if (!SetupPixelFormat()) 
	{
		// return;
        // PostQuitMessage (0); 
	}
	
	
    HGLRC ghRC = wglCreateContext(GetDC()->GetSafeHdc()); 
	if(ghRC != NULL)
	{
		m_bOglInitialized = true;
	}
	else
	{
		DWORD nCurrError = GetLastError();
		TRACE("CreateContext failed %d\n", nCurrError);
	}
    wglMakeCurrent(GetDC()->GetSafeHdc(), ghRC); 
    GetClientRect(&m_rect); 
	// TRACE("%d %d -- %d %d\n", m_rect.right, m_rect.top, m_rect.left, m_rect.bottom);
    initializeGL(m_rect.right, m_rect.bottom);
	return ghRC;
}

int CCarSimDisplayDlg::CarSimDrawButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	int nResult = CButton::OnCreate(lpCreateStruct);
	
	// InitOglStuff();
	
	return nResult;
}

GLfloat lightAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f };
GLfloat lightDiffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat lightSpecular[] = { 0.9f, 0.9f, 0.9f };
GLfloat materialColor[] = { 0.8f, 0.0f, 0.0f };
// GLfloat lightpos[] = { -80.0f, 120.0f, 100.0f, 0.0f };
GLfloat lightpos[] = { 150.0f, 20.0f, 100.0f, 1.0f };

void CCarSimDisplayDlg::CarSimDrawButton::DrawGlScene(void) 
{ 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode( GL_MODELVIEW );


	{
		glMatrixScope Scope1;
		
		// glLoadIdentity();
		// glClear( GL_ALL_ATTRIB_BITS | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 

	 
		glLightfv(GL_LIGHT0,GL_POSITION, lightpos);
		glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glMaterialfv(GL_FRONT, GL_SPECULAR,lightSpecular);
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, materialColor);
		glMateriali(GL_FRONT, GL_SHININESS,128); 

		// ::glDisable(GL_DEPTH_TEST);

		// TRACE("Supposedly cleared\n");
	     
		// glLoadIdentity();

		static int nColorCnt = 0;
		++nColorCnt;

		// glColor3d(1.0-0.5*sin(0.1*nColorCnt), 0.0, 1.0-0.5*cos(0.1*nColorCnt)); // Blue
		glColor3d(0.1, 0.0, 1.0);
		
		{
			glMatrixScope Scope2;
			glLoadIdentity();
			
			glTranslated(-100.0, -100.0, 0.0);
			for(int nXSquare = 0; nXSquare < 20; ++nXSquare)
			{
				{
					glMatrixScope Scope3;
					for(int nYSquare = 0; nYSquare < 20; ++nYSquare)
					{
						glBegin(GL_QUADS);
						glVertex3d(4.0, 4.0, 0.0);
						glVertex3d(-4.0, 4.0, 0.0);
						glVertex3d(-4.0, -4.0, 0.0);
						glVertex3d(4.0, -4.0, 0.0);

						glEnd();

						double currMat[16];
						::glGetDoublev(GL_MODELVIEW_MATRIX, currMat);

						glBegin(GL_QUADS);
						glVertex3d(-4.0, 4.0, -1.0);
						glVertex3d(4.0, 4.0, -1.0);
						glVertex3d(4.0, -4.0, -1.0);
						glVertex3d(-4.0, -4.0, -1.0);

						glEnd();
						glTranslated(0.0, 10.0, 0.0);
					}
				}				
				glTranslated(10.0, 0.0, 0.0);
			}

		}

		g_MyRobot.DrawMe();
	 
	/*        latitude += latinc; 
			longitude += longinc; 
	 
			polarView( radius, 0, latitude, longitude ); 
	 
			glIndexi(RED_INDEX); 
			glCallList(CONE); 
	 
			glIndexi(BLUE_INDEX); 
			glCallList(GLOBE);*/ 
	 
	  
			//glPushMatrix(); 
			//    glTranslatef(0.8F, -0.65F, 0.0F); 
			//    glRotatef(30.0F, 1.0F, 0.5F, 1.0F); 
			//    // glCallList(CYLINDER); 
			//glPopMatrix(); 
	 
	}
 
	BOOL bRet = SwapBuffers(GetDC()->GetSafeHdc());
	if(bRet)
	{
		// TRACE("SwapBuffers returned True\n");
	}
	else
	{
		TRACE("SwapBuffers returned False\n");
	}
}


void CCarSimDisplayDlg::CarSimDrawButton::DrawScreen()
{
	
	HGLRC glRc = InitOglStuff();
	
	
	DrawGlScene();
	wglMakeCurrent(NULL, NULL);
	wglDeleteContext(glRc);
	
	// 	TRACE("Draw stuff\n");
	
}

void CCarSimDisplayDlg::CarSimDrawButton::OnPaint()
{
	// CButton::OnPaint();
	BeginPaint(&m_ps);
	DrawScreen();
	EndPaint(&m_ps);
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CCarSimDisplayDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
		//m_buttonToDrawOn.OnPaint();
		TRACE("App on paint\n");
		// m_buttonToDrawOn.DrawScreen();
		// ::Gdi
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CCarSimDisplayDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CCarSimDisplayDlg::OnBnClickedOk()
{
	// TODO: Add your control notification handler code here

	static char szFilter[] = "Nnet Files (*.nnet)|*.nnet|Data Files (*.dat)|*.dat|Text Files (*.txt)|*.txt|All Files (*.*)|*.*||";

	CFileDialog dlgOutputSelect(FALSE, NULL, 
		NULL, OFN_HIDEREADONLY,
		szFilter,
		NULL,
		0);

	if(dlgOutputSelect.DoModal() == IDOK)
	{
		CString strFileForWriting = dlgOutputSelect.GetPathName();
		g_MyRobot.SaveNeuralNet(static_cast<const char *>(strFileForWriting));
	}

	TRACE("ON OK\n");
	OnOK();
	
}

void CCarSimDisplayDlg::OnBnClickedCancel()
{
	// TODO: Add your control notification handler code here
	OnCancel();
}

void CCarSimDisplayDlg::OnBnClickedButtonToDrawOn()
{
	// TODO: Add your control notification handler code here
	// OnPaint();
	m_buttonToDrawOn.DrawScreen();
	
}

void CCarSimDisplayDlg::OnBnClickedButtonForwards()
{
	// TODO: Add your control notification handler code here
	int nRelPos = m_sliderSteering.GetPos() - m_sliderSteering.GetRangeMin();
	int nRangeSize = m_sliderSteering.GetRangeMax() - m_sliderSteering.GetRangeMin();
	double lfRelPosNormed = (0.5*nRangeSize-nRelPos)/(1.0*nRangeSize);
	int nRelPosByFive = static_cast<int>(10.0*lfRelPosNormed);

	g_MyRobot.Move(nRelPosByFive, 2);

	m_buttonToDrawOn.DrawScreen();
}

void CCarSimDisplayDlg::OnBnClickedButtonBackwards()
{
	// TODO: Add your control notification handler code here
	int nRelPos = m_sliderSteering.GetPos() - m_sliderSteering.GetRangeMin();
	int nRangeSize = m_sliderSteering.GetRangeMax() - m_sliderSteering.GetRangeMin();
	double lfRelPosNormed = (0.5*nRangeSize-nRelPos)/(1.0*nRangeSize);
	int nRelPosByFive = static_cast<int>(10.0*lfRelPosNormed);

	g_MyRobot.Move(nRelPosByFive, -2);

	m_buttonToDrawOn.DrawScreen();
}

void CCarSimDisplayDlg::OnBnClickedButtonGenObstacles()
{
	// TODO: Add your control notification handler code here
	// m_textboxNumObstacles
	// m_textboxSidesPerObstacle
	CString strNumObs;
	CString strSidesPerObs;
	CString strAvgRadius;
	m_textboxNumObstacles.GetWindowText(strNumObs);
	m_textboxSidesPerObstacle.GetWindowText(strSidesPerObs);
	m_textboxRadiusPerObstacle.GetWindowText(strAvgRadius);

	int nNumObs = -1;
	int nNumSides = -1;
	double lfAvgRadius = -1.0;
	int nScanResult = sscanf((const char *)strNumObs, "%d", &nNumObs);
	CString strErr;
	if(nScanResult != 1)
	{
		strErr.Format("\"%s\" is not a valid number of obstacles", strNumObs);
		AfxMessageBox(strErr);
		return;
	}

	nScanResult = sscanf((const char *)strSidesPerObs, "%d", &nNumSides);
	if(nScanResult != 1)
	{
		strErr.Format("\"%s\" is not a valid number of sides per obstacle", strSidesPerObs);
		AfxMessageBox(strErr);
		return;
	}

	nScanResult = sscanf((const char *)strAvgRadius, "%lf", &lfAvgRadius);
	if(nScanResult != 1)
	{
		strErr.Format("\"%s\" is not a valid obstacle radius", strAvgRadius);
		AfxMessageBox(strErr);
		return;
	}

	if(nNumObs < 0 || nNumObs >= 8)
	{
		AfxMessageBox("Number of obstacles must be between 0 and 7 (inclusive)");
		return;
	}

	if(nNumSides < 3 || nNumSides > 10)
	{
		AfxMessageBox("Sides per obstacle must be between 3 and 10 (inclusive)");
		return;
	}

	if(lfAvgRadius < 5.0 || lfAvgRadius > 25.0)
	{
		AfxMessageBox("Average Obstacle Radius must be between 5.0 and 25.0 (inclusive)");
		return;
	}

	double p2ObsMin[] = {-50.0, -50.0};
	double p2ObsMax[] = {50.0, 50.0};
	g_MyRobot.SetAllObstacleParams(nNumObs, nNumSides, p2ObsMin, p2ObsMax, lfAvgRadius);

	m_buttonToDrawOn.DrawScreen();
}

void CCarSimDisplayDlg::OnBnClickedButtonRunBehavior()
{
	// TODO: Add your control notification handler code here
	if(!m_bPlaybackRunning)
	{
		m_bPlaybackRunning = true;
		SetTimer(GetPlaybackTimer(), 100, NULL);
		TRACE("Started playback\n");
	}
}

void CCarSimDisplayDlg::OnBnClickedButtonStopPlayback()
{
	// TODO: Add your control notification handler code here
	if(m_bPlaybackRunning)
	{
		m_bPlaybackRunning = false;
		KillTimer(GetPlaybackTimer());
		TRACE("Stopped playback\n");
	}
}

void CCarSimDisplayDlg::OnBnClickedButtonTrain()
{
	// TODO: Add your control notification handler code here

	CString strNumTrainRuns;
	m_editNumTrainingRuns.GetWindowText(strNumTrainRuns);
	CString strEvalFreq;
	m_editEvalFreq.GetWindowText(strEvalFreq);

	int nNumTrainingRuns, nEvalFreq;

	int nScanResult = sscanf((const char *)strNumTrainRuns, "%d", &nNumTrainingRuns);
	if(nScanResult != 1)
	{
		CString strErr;
		strErr.Format("\"%s\" is not a valid number of training runs", strNumTrainRuns);
		AfxMessageBox(strErr);
		return;
	}

	if(nNumTrainingRuns < 1 || nNumTrainingRuns > 10000000)
	{
		AfxMessageBox("Number of training runs must be between 1 and 10000000 (inclusive)");
		return;
	}

	nScanResult = sscanf((const char *)strEvalFreq, "%d", &nEvalFreq);
	if(nScanResult != 1)
	{
		CString strErr;
		strErr.Format("\"%s\" is not a valid evaluation frequency", strNumTrainRuns);
		AfxMessageBox(strErr);
		return;
	}

	CString strLearnRate;
	m_editLearningRate.GetWindowText(strLearnRate);
	double lfLearningRate;
	nScanResult = sscanf((const char *)strLearnRate, "%lf", &lfLearningRate);
	if(nScanResult != 1)
	{
		CString strErr;
		strErr.Format("\"%s\" is not a valid learning rate", strLearnRate);
		AfxMessageBox(strErr);
		return;
	}

	if(lfLearningRate > 0.9 || lfLearningRate <= 0.001)
	{
		AfxMessageBox("Learning rate must be in the range 0.001 to 0.9");
	}

	CString strDiscountFactor;
	
	m_editDiscountFactor.GetWindowText(strDiscountFactor);
	double lfRewardDiscount;
	nScanResult = sscanf((const char *)strDiscountFactor, "%lf", &lfRewardDiscount);
	if(nScanResult != 1)
	{
		CString strErr;
		strErr.Format("\"%s\" is not a valid reward discount rate", strDiscountFactor);
		AfxMessageBox(strErr);
		return;
	}

	if(lfRewardDiscount > 0.999 || lfRewardDiscount <= 0.001)
	{
		AfxMessageBox("Reward Discount factor must be in the range 0.001 to 0.999");
	}

	CString strMagicAnneal;
	double lfMagicAnneal = 200.0;
	m_editMagicAnneal.GetWindowText(strMagicAnneal);

	nScanResult = sscanf((const char *)strMagicAnneal, "%lf", &lfMagicAnneal);
	if(nScanResult != 1 && !strMagicAnneal.IsEmpty())
	{
		CString strErr;
		strErr.Format("The Annealing Magic number is related to the number of iterations \
					  before the robot stops \"exploring\" and starts using more learning \
					  (default is 200)");
		AfxMessageBox(strErr);
		
	}

	if(lfMagicAnneal < 50.0)
	{
		CString strErr;
		strErr.Format("The Annealing Magic number is related to the number of iterations \
					  before the robot stops \"exploring\" and starts using more learning \
					  (default is 200)");
		AfxMessageBox(strErr);
	}

	g_MyRobot.SetMagicAnneal(lfMagicAnneal);

	static char szFilter[] = "Data Files (*.dat)|*.dat|Text Files (*.txt)|*.txt|All Files (*.*)|*.*||";

	CFileDialog dlgOutputSelect(FALSE, NULL, 
		NULL, OFN_HIDEREADONLY,
		szFilter,
		NULL,
		0);

	dlgOutputSelect.DoModal();

	CString strFileForWriting = dlgOutputSelect.GetPathName();

	//	explicit CFileDialog(
	//   BOOL bOpenFileDialog,
	//   LPCTSTR lpszDefExt = NULL,
	//   LPCTSTR lpszFileName = NULL,
	//   DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
	//   LPCTSTR lpszFilter = NULL,
	//   CWnd* pParentWnd = NULL,
	//   DWORD dwSize = 0
	//);

	g_MyRobot.SetQlearnState(m_checkboxUseQlearning.GetCheck());
	g_MyRobot.Train(nNumTrainingRuns, strFileForWriting, nEvalFreq, lfLearningRate, lfRewardDiscount);
}

void CCarSimDisplayDlg::OnBnClickedButtonSaveNnet()
{
	// TODO: Add your control notification handler code here

	static char szFilter[] = "Nnet Files (*.nnet)|*.nnet|Data Files (*.dat)|*.dat|Text Files (*.txt)|*.txt|All Files (*.*)|*.*||";

	CFileDialog dlgOutputSelect(FALSE, NULL, 
		NULL, OFN_HIDEREADONLY,
		szFilter,
		NULL,
		0);

	dlgOutputSelect.DoModal();

	CString strFileForWriting = dlgOutputSelect.GetPathName();
	g_MyRobot.SaveNeuralNet(static_cast<const char *>(strFileForWriting));
}

void CCarSimDisplayDlg::OnBnClickedButtonLoadNnet()
{
	// TODO: Add your control notification handler code here

	static char szFilter[] = "Nnet Files (*.nnet)|*.nnet|Data Files (*.dat)|*.dat|Text Files (*.txt)|*.txt|All Files (*.*)|*.*||";

	CFileDialog dlgOutputSelect(TRUE, NULL, 
		NULL, OFN_HIDEREADONLY,
		szFilter,
		NULL,
		0);

	dlgOutputSelect.DoModal();

	CString strFileForReading = dlgOutputSelect.GetPathName();

	g_MyRobot.LoadNeuralNet(static_cast<const char *>(strFileForReading));
}

void CCarSimDisplayDlg::OnBnClickedButtonResetRobot()
{
	// TODO: Add your control notification handler code here
	g_MyRobot.SetRobotAndGoal();
	m_buttonToDrawOn.DrawScreen();
}
